package com.me.study.concurrent.threadUtil;

import java.time.LocalTime;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

import com.me.common.util.SleepUtil;

/**
 * <pre> Fork/Join 框架测试：有返回值的任务 -- RecursiveTask </pre>
 * 1, 当业务处理很简单，抵消不了多线程上下文切换带来的性能损耗时，单线程性能更好！
 * <blockquote>
 *      比如：注释掉 SleepUtil.ms(1);
 * </blockquote>
 * 2, 当业务处理变得复杂时，才能发挥分治算法的优势！
 *
 * @author ME
 * @date 2019/2/2 16:08
 */
public class TestForkJoinPool {

    private static final int ARRAY_LENGTH = 4000;
    private static final int[] INT_ARR = new int[ARRAY_LENGTH];

    static {
        for (int i = 0; i < INT_ARR.length; i++) {
            INT_ARR[i] = i;
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("==============单线程求和");
        int count = 0;
        long start = System.currentTimeMillis();
        for (int i = 0; i < INT_ARR.length; i++) {
            SleepUtil.ms(1);
            count += INT_ARR[i];
        }
        System.out.println("The count is [" + count + "], spend time: " + (System.currentTimeMillis() - start) + "ms\n");

        System.out.println("==============Fork/Join 求和");
        long start2 = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        SumTask task = new SumTask(INT_ARR, 0, INT_ARR.length - 1);
        /**
         * 提交任务3个方法 execute、invoke、submit 的区别：
         *      execute(ForkJoinTask) 异步执行 tasks，无返回值
         * 	    invoke(ForkJoinTask) 自带 join(), tasks会被同步到主进程。阻塞等待任务完成并返回结果，或者抛出异常
         *      submit(ForkJoinTask) 异步执行，且带Task返回值，可通过 task.get() 实现同步到主线程
         */
        System.out.println(LocalTime.now());
        // pool.invoke(task);// 阻塞主线程
        ForkJoinTask<Integer> submit = pool.submit(task);
        System.out.println(LocalTime.now() + "      你猜 main线程有没有被阻塞");
        System.out.println("The count is [" + task.get() + "], spend time: " + (System.currentTimeMillis() - start2) + "ms\n");
        System.out.println("The count is [" + task.join() + "], spend time: " + (System.currentTimeMillis() - start2) + "ms\n");
    }

    /**
     * 无返回值的 Fork/Join 子任务
     */
    private static class SumTask extends RecursiveTask<Integer> {
        /**
         * 临界点，每个小任务的任务梳理超过临界点就需要再次拆分
         */
        private static final int THRESHOLD = ARRAY_LENGTH / 10;
        /**
         * 原数组的指针，避免了复制的消耗
         */
        private final int[] src;
        /**
         * 当前任务的开始下标
         */
        private final int startInd;
        /**
         * 当前任务的结束下标
         */
        private final int endInd;

        public SumTask(int[] src, int startInd, int endInd) {
            this.src = src;
            this.startInd = startInd;
            this.endInd = endInd;
        }

        @Override
        protected Integer compute() {
            // if (endInd - startInd + 1 <= THRESHOLD) {
            if (endInd - startInd < THRESHOLD) {
                // 任务数量小于临界点数量，直接执行
                int count = 0;
                for (int index = startInd; index <= endInd; index++) {
                    SleepUtil.ms(1);
                    count += src[index];
                }
                return count;
            } else {
                // 任务数量大于于临界点数量，直接执行
                int middle = (endInd + startInd) / 2;
                SumTask task1 = new SumTask(src, startInd, middle);
                SumTask task2 = new SumTask(src, middle + 1, endInd);
                invokeAll(task1, task2);
                return task1.join() + task2.join();
            }
        }
    }

}
